/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.situations;

import java.util.Arrays;

import robocup.component.DAInamiteConstants;

/**
 * This class sums up the results of the {@link PassEvaluator}.
 * 
 * The {@link PassEvaluator} tries to find out if a pass to a given point 
 * succeeds without the oppponent intercepting the ball before. More than that
 * it optimizes the result trying to pass through opponents and keep the ball 
 * trajectory as far away from interception as possible. By now this is only 
 * used for the {@link ScoreSituation} when trying to find the best way for 
 * scoring.<br>
 * <br>
 * The most important Results:
 * <ul>
 * <li>Opponents intercepting the pass from the left|right side</li>
 * <li>max. overlapping angle if opponent tries interception (right/left)</li>
 * <li>max. overlapping distance if opponent tries interception (right/left)
 * </li>
 * <li>....</li>
 * </ul
 */
public class PassEvaluatorResults {

	/**
	 * creates an array of instances.
	 * 
	 * @param length -
	 *            array length
	 * @return
	 */
	public static PassEvaluatorResults[] getInstances(int length) {
		PassEvaluatorResults[] results = new PassEvaluatorResults[length];
		for (int i = 0; i < results.length; i++) {
			results[i] = new PassEvaluatorResults();
		}
		return results;
	}

	// horizont
	private int maxCycle = DAInamiteConstants.PROPHET_MAX_CYCLES_PASSING;

	public static double EMPTY_VALUE = Double.MAX_VALUE;

	// if an opponent gets the pass by mopre than MAX_OVERLAP degrees
	// the calculation stops
	public static double MAX_OVERLAP = -20.0;

	// opponent is on left side of pass line
	private boolean[] isLeft = new boolean[11];

	// the nearest opponent from the left side has this angle
	// to the passline (by cycle)
	private double[] angleLeft = new double[this.maxCycle];

	// the nearest opponent from the right side has this angle
	// to the passline (by cycle)
	private double[] angleRight = new double[this.maxCycle];

	// same as above but now with distances instead of angles
	private double[] distanceLeft = new double[this.maxCycle];

	private double[] distanceRight = new double[this.maxCycle];

	// minAngle by opponent left side
	private double[] opponentAngleLeft = new double[11];

  // minAngle by opponent right side
	private double[] opponentAngleRight = new double[11];

	// no passing possible
	private boolean noPassing = true;

	// min(angleLeft) (=min(opponentAngleLeft))
	private double minAngleLeft = PassEvaluatorResults.EMPTY_VALUE;

	// min(angleRight) (=min(opponentAngleRight))
	private double minAngleRight = PassEvaluatorResults.EMPTY_VALUE;

	// min overlapping distance left
	private double minDistanceLeft = PassEvaluatorResults.EMPTY_VALUE;

  // min overlapping distance right
	private double minDistanceRight = PassEvaluatorResults.EMPTY_VALUE;

	// the opponent intercepts and is on the left side
	private boolean[] interceptingOpponentsLeft = new boolean[11];

  // the opponent intercepts and is on the right side
	private boolean[] interceptingOpponentsRight = new boolean[11];

	/**
	 * Constructor of an object, which encapsulates the the results of a given
	 * simulated pass.
	 */
	public PassEvaluatorResults() {
		this.reset();
	}

	/**
	 * resets all values
	 * 
	 */
	public void reset() {
		Arrays.fill(this.isLeft, false);
		Arrays.fill(this.angleLeft, PassEvaluatorResults.EMPTY_VALUE);
		Arrays.fill(this.angleRight, PassEvaluatorResults.EMPTY_VALUE);
		Arrays.fill(this.distanceLeft, PassEvaluatorResults.EMPTY_VALUE);
		Arrays.fill(this.distanceRight, PassEvaluatorResults.EMPTY_VALUE);
		Arrays.fill(this.opponentAngleLeft, PassEvaluatorResults.EMPTY_VALUE);
		Arrays.fill(this.opponentAngleRight, PassEvaluatorResults.EMPTY_VALUE);
		Arrays.fill(this.interceptingOpponentsLeft, false);
		Arrays.fill(this.interceptingOpponentsRight, false);
		this.minAngleLeft = PassEvaluatorResults.EMPTY_VALUE;
		this.minAngleRight = PassEvaluatorResults.EMPTY_VALUE;
		this.minDistanceLeft = PassEvaluatorResults.EMPTY_VALUE;
		this.minDistanceRight = PassEvaluatorResults.EMPTY_VALUE;
		this.noPassing = false;
	}

	/**
	 * test if a double represents the unknown value.
	 */
	public boolean isUnknown(double d) {
		return d == PassEvaluatorResults.EMPTY_VALUE;
	}

	/**
	 * stores the information that an opponent is on the left side of the pass
	 * line.
	 * 
	 * @param playerIndex
	 * @param left
	 */
	public void setIsLeft(int playerIndex, boolean left) {
		this.isLeft[playerIndex] = left;
	}

	/**
	 * opponent is on left side of pass line.
	 * 
	 * @param playerIndex
	 * @return
	 */
	public boolean isLeft(int playerIndex) {
		return this.isLeft[playerIndex];
	}

	/**
	 * sets the angle overlapping information.
	 * 
	 * @param playerIndex - the player by its index
	 * @param cycle - the cycle
	 * @param angle - the angle
   * 
   * @see PassEvaluator
	 */
	public void setAngle(int playerIndex, int cycle, double angle) {
		if (this.isLeft(playerIndex)) {
			this.angleLeft[cycle] = Math.min(this.angleLeft[cycle], angle);
			this.opponentAngleLeft[playerIndex] = Math.min(
					this.opponentAngleLeft[playerIndex], angle);
			this.minAngleLeft = Math.min(this.minAngleLeft, angle);
		} else {
			this.angleRight[cycle] = Math.min(this.angleRight[cycle], angle);
			this.opponentAngleRight[playerIndex] = Math.min(
					this.opponentAngleRight[playerIndex], angle);
			this.minAngleRight = Math.min(this.minAngleRight, angle);
		}
	}

	/**
	 * gets the angle by player.
	 * 
	 * @param playerIndex - the player's index
	 * @return the angle of the player rel. to the pass trajectory
	 */
	public double getAngleByPlayer(int playerIndex) {
		if (this.isLeft(playerIndex)) {
			return this.angleLeft[playerIndex];
		} else {
			return this.angleRight[playerIndex];
		}
	}

	/**
	 * sets the distance by player.
	 * 
	 * @param playerIndex - the player by its index
	 * @param distance - the min. distance a player gets near the trajectory. If 
   * <code>distance</code> the player is able to intercept the pass.
	 */
	public void setDistance(int playerIndex, double distance) {
		if (this.isLeft(playerIndex)) {
			this.distanceLeft[playerIndex] = Math.min(
					this.distanceLeft[playerIndex], distance);
			this.minDistanceLeft = Math.min(this.minDistanceLeft, distance);
		} else {
			this.distanceRight[playerIndex] = Math.min(
					this.distanceRight[playerIndex], distance);
			this.minDistanceRight = Math.min(this.minDistanceRight, distance);
		}
	}

	/**
	 * @return Returns true if there is no secure passing through the opponents.
	 */
	public boolean isNoPassing() {

		return this.noPassing || (this.minAngleLeft + this.minAngleRight < 0.0);
	}

	/**
	 * @param noPassing
	 *            The noPassing to set.
	 */
	public void setNoPassing(boolean noPassing) {
		this.noPassing = noPassing;
	}

	/**
	 * tells if the evaluation is finsished.
	 * 
	 * @return true if finished
	 */
	public boolean isFinished() {
		if (this.minAngleLeft == PassEvaluatorResults.EMPTY_VALUE
				|| this.minAngleRight == PassEvaluatorResults.EMPTY_VALUE) {
			return false;
		}
		return this.minAngleLeft + this.minAngleRight < PassEvaluatorResults.MAX_OVERLAP;
	}

	/**
	 * @return Returns the minimum relative angle opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the left side of the trajectory are considered.
	 */
	public double getMinAngleLeft() {
		return this.minAngleLeft;
	}

	/**
   * @return Returns the minimum relative angle opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the right side of the trajectory are considered.
	 */
	public double getMinAngleRight() {
		return this.minAngleRight;
	}

	/**
   * @return Returns the minimum distance opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the left side of the trajectory are considered.
	 */
	public double getMinDistanceLeft() {
		return this.minDistanceLeft;
	}

	/**
   * @return Returns the minimum distance opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the right side of the trajectory are considered.
	 */
	public double getMinDistanceRight() {
		return this.minDistanceRight;
	}

	/**
	 * @return Returns the angles opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the left side of the trajectory are considered.
	 */
	public double[] getAngleLeft() {
		return this.angleLeft;
	}

	/**
   * @return Returns the angles opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the right side of the trajectory are considered.
	 */
	public double[] getAngleRight() {
		return this.angleRight;
	}

	/**
   * @return Returns the distances opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the left side of the trajectory are considered.

	 */
	public double[] getDistanceLeft() {
		return this.distanceLeft;
	}

	/**
   * @return Returns the distances opponents can come near the pass 
   * trajectory. If <0, the opponent might be able to intercept the pass.
   * 
   * Only opponents on the right side of the trajectory are considered.
	 */
	public double[] getDistanceRight() {
		return this.distanceRight;
	}

	/**
	 * @return Returns whether an opponent is on the left side of the trajectory 
   * for all opponents.
	 */
	public boolean[] getIsLeft() {
		return this.isLeft;
	}

	/**
	 * @return Returns the angle to an opponent on the left side of the 
   * trajectory.
	 */
	public double[] getOpponentAngleLeft() {
		return this.opponentAngleLeft;
	}

	/**
   * @return Returns the angle to an opponent on the right side of the 
   * trajectory.
	 */
	public double[] getOpponentAngleRight() {
		return this.opponentAngleRight;
	}

	/**
	 * the angle that can't be reached by opponents. (relative to the pass 
   * trajectory)
	 * 
	 * @return angle (<0 if opponents get all passes)
	 */
	public double getFreeAngle() {
		if (this.minAngleLeft == PassEvaluatorResults.EMPTY_VALUE
				|| this.minAngleRight == PassEvaluatorResults.EMPTY_VALUE) {
			return PassEvaluatorResults.EMPTY_VALUE;
		}
		return this.minAngleLeft + this.minAngleRight;
	}

	/**
	 * the opponents on the left side of the pass line that are able to get near
	 * the pass
	 */
	public boolean[] getOpponentIsInterceptingLeft() {
		for (int i = 0; i < this.interceptingOpponentsLeft.length; i++) {
			this.interceptingOpponentsLeft[i] = this.opponentAngleLeft[i] != PassEvaluatorResults.EMPTY_VALUE
					&& this.isLeft(i);
		}
		return this.interceptingOpponentsLeft;
	}

	/**
	 * the opponents on the right side of the pass line that are able to get
	 * near the pass
	 */
	public boolean[] getOpponentIsInterceptingRight() {
		for (int i = 0; i < this.interceptingOpponentsRight.length; i++) {
			this.interceptingOpponentsRight[i] = this.opponentAngleRight[i] != PassEvaluatorResults.EMPTY_VALUE
					&& !this.isLeft(i);
		}
		return this.interceptingOpponentsRight;
	}

	/**
	 * the max deviation of the pass line to the left side. (in most cases that
	 * sould be the angle to the next opponent relative to the pass angle)
	 * 
	 * @param deviation - the max angle to consider opponets on the left side of
   * the pass line
	 */
	public void setInitAngleLeft(double deviation) {
		this.minAngleLeft = deviation;
	}

	/**
	 * the max deviation of the pass line to the right side. (in most cases that
	 * sould be the angle to the next opponent relative to the pass angle)
	 * 
   * @param deviation - the max angle to consider opponets on the right side of
   * the pass line
	 */
	public void setInitRightAngle(double deviation) {
		this.minAngleRight = deviation;
	}

	/**
	 * the middle between the intercepting opponents by angle. (the angle the
	 * pass should be corrected by for being farest from interception)
	 * 
	 * @return
	 */
	public double getAvgAngle() {
		return (-this.minAngleLeft + this.minAngleRight) / 2.0;
	}

	/**
	 * Fortunatly there was a plugin for that.... :-)
	 * 
	 * toString methode: creates a String representation of the object
	 * 
	 * @return the String representation
	 * @author info.vancauwenberge.tostring plugin
	 */
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("PassEvaluatorResults[");
		buffer.append("maxCycle = ").append(this.maxCycle);
		buffer.append(", EMPTY_VALUE = ").append(EMPTY_VALUE);
		buffer.append(", MAX_OVERLAP = ").append(MAX_OVERLAP);
		if (this.isLeft == null) {
			buffer.append(", isLeft = ").append("null");
		} else {
			buffer.append(", isLeft = ").append("[");
			for (int i = 0; i < this.isLeft.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.isLeft[i]);
			}
			buffer.append("]");
		}
		if (this.angleLeft == null) {
			buffer.append(", angleLeft = ").append("null");
		} else {
			buffer.append(", angleLeft = ").append("[");
			for (int i = 0; i < this.angleLeft.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.angleLeft[i]);
			}
			buffer.append("]");
		}
		if (this.angleRight == null) {
			buffer.append(", angleRight = ").append("null");
		} else {
			buffer.append(", angleRight = ").append("[");
			for (int i = 0; i < this.angleRight.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.angleRight[i]);
			}
			buffer.append("]");
		}
		if (this.distanceLeft == null) {
			buffer.append(", distanceLeft = ").append("null");
		} else {
			buffer.append(", distanceLeft = ").append("[");
			for (int i = 0; i < this.distanceLeft.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.distanceLeft[i]);
			}
			buffer.append("]");
		}
		if (this.distanceRight == null) {
			buffer.append(", distanceRight = ").append("null");
		} else {
			buffer.append(", distanceRight = ").append("[");
			for (int i = 0; i < this.distanceRight.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.distanceRight[i]);
			}
			buffer.append("]");
		}
		if (this.opponentAngleLeft == null) {
			buffer.append(", opponentAngleLeft = ").append("null");
		} else {
			buffer.append(", opponentAngleLeft = ").append("[");
			for (int i = 0; i < this.opponentAngleLeft.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.opponentAngleLeft[i]);
			}
			buffer.append("]");
		}
		if (this.opponentAngleRight == null) {
			buffer.append(", opponentAngleRight = ").append("null");
		} else {
			buffer.append(", opponentAngleRight = ").append("[");
			for (int i = 0; i < this.opponentAngleRight.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.opponentAngleRight[i]);
			}
			buffer.append("]");
		}
		buffer.append(", noPassing = ").append(this.noPassing);
		buffer.append(", minAngleLeft = ").append(this.minAngleLeft);
		buffer.append(", minAngleRight = ").append(this.minAngleRight);
		buffer.append(", minDistanceLeft = ").append(this.minDistanceLeft);
		buffer.append(", minDistanceRight = ").append(this.minDistanceRight);
		if (this.interceptingOpponentsLeft == null) {
			buffer.append(", interceptingOpponentsLeft = ").append("null");
		} else {
			buffer.append(", interceptingOpponentsLeft = ").append("[");
			for (int i = 0; i < this.interceptingOpponentsLeft.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.interceptingOpponentsLeft[i]);
			}
			buffer.append("]");
		}
		if (this.interceptingOpponentsRight == null) {
			buffer.append(", interceptingOpponentsRight = ").append("null");
		} else {
			buffer.append(", interceptingOpponentsRight = ").append("[");
			for (int i = 0; i < this.interceptingOpponentsRight.length; i++) {
				if (i != 0)
					buffer.append(", ");
				buffer.append(this.interceptingOpponentsRight[i]);
			}
			buffer.append("]");
		}
		buffer.append("]");
		return buffer.toString();
	}

}
